#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <assert.h>
#include <iostream>
#include <string.h>
#include <map>
#include <fstream>
#include <set>

using namespace std;

extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;

//we assign a default value for N
int N = 3;

//the class of recording the word and its count
class Word
{
public:
	string word;
	int count;
public:
	Word(void) : count(1) {}

	bool operator < (const Word & word) const
	{
		if(this->count > word.count)
			return true;
		else if (this->count < word.count)
			return false;
		else	
			return (this->word).compare(word.word) <= 0 ? true : false;
	}
};

//This is the dictionary we maintain in order to store the map between each distinct word and its number of occurance in a file.
class Dict
{
public:
	map<string, Word> dict;
	string filename;
public:
	Dict(string fn) : filename(fn) {}
	void Add(char * word);
	void ShowMostCommonWords(int n = N);
	~Dict();
};

void Dict::ShowMostCommonWords(int n)
{
	set <Word> words;

	for(map<string, Word>::iterator i = dict.begin(); i != dict.end(); i++)
		words.insert(i->second);

	for(set <Word>::iterator i = words.begin(); i != words.end(); ++i)
	{
		if(--n >= 0)
			cout<<filename<<","<<i->word<<","<<i->count<<endl;
		else
			return;
	}
}

void Dict::Add(char * word)
{
	assert(word);
	if(strcmp(word, "") == 0)
		return;
	
  	for (int i = 0; word[i]; i++)
	    word[i] = tolower(tolower(word[i]));

	string word_str(word);
	map<string, Word>::iterator map_ptr;
	map_ptr = dict.find(word_str);
	
	if(map_ptr != dict.end())
	{
		Word & word = dict[word_str];
		word.count++;
	}
	else
	{
		Word * word = new Word();
		word->word = word_str;
		dict[word_str] = *word;
	}
}

Dict::~Dict()
{
	for(map<string, Word>::iterator wi = dict.begin(); wi != dict.end(); wi++)
		delete [] &(wi->second);
}

void process_each_file(string filename)
{
	fstream file(filename.c_str(), ios::in);
	if(!file)
	{
		cerr<<"file "<<filename<<" cannot be open!";
		return;
	}
	
	Dict * pdict = new Dict(filename);

	string buffer;
	while(getline(file, buffer))
	{
		char * c_buffer = new char [buffer.size() + 1];
		strcpy(c_buffer, buffer.c_str());

		char * word = strtok(c_buffer, " \t\n\v\f\r.?!:;-()[]'\"/,");

		while(word)
		{
			pdict->Add(word);
			word = strtok(NULL, " \t\n\v\f\r.?!:;-()[]'\"/,");
		}

		delete [] c_buffer;
	}

	pdict->ShowMostCommonWords(N);

	delete [] pdict;
}

int main(int argc, char ** argv)
{
	optind = 1;
	opterr = 0;
	int result;
	while((result = getopt(argc, argv, "n:")) != -1)
	{
		switch((char)result)
		{
			case 'n':
				N = atoi(optarg); 
				break;
			case '?':
				if (optopt == 'n')
					fprintf(stderr, "Option -%c requires an argument.\n", optopt);
				else if (isprint (optopt))
					fprintf(stderr, "Unknown option '-%c'.\n", optopt);
				else
					fprintf(stderr, "Unkown option character '\\x%x'.\n", optopt);
				return 1;
			default:
				abort();

		}
	}

	//Here we sucessfully get the value of N, which indicates the number of the most common words we need to output.
	//printf("N = %d\n", N);

	if(optind == argc) //there is no file(s) that need to be processed
		return 0;
	for(int i = optind; i < argc; i++)
		process_each_file(string(argv[i]));

	return 0;
}

